###############################################################################
# SuperLinks                                                                  #
# Written by Gregory Nolle (greg@nolle.co.uk)                                 #
# Copyright 2001-2002 by PlasmaPulse Solutions (http://www.plasmapulse.com)   #
###############################################################################
# SearchTools.pm.pl -> SearchTools library                                    #
###############################################################################
# DON'T EDIT BELOW UNLESS YOU KNOW WHAT YOU'RE DOING!                         #
###############################################################################
package SearchTools;

sub new {
  my ($class) = @_;
  my $self    = {};
  return bless ($self, $class);
}

sub DESTROY {}

###############################################################################
# BuildSQL subroutine
sub BuildSQL {
  my $self = shift;
  my %in = (
    "tokens"  => undef,
    "columns" => [],
    "case"    => 0,
    "whole"   => 0,
    "like"    => 0,
    @_
  );

  $in{'tokens'} = $self->ParseQuery($in{'tokens'}) unless (ref($in{'tokens'}));

  my @Where;
  foreach my $field (@{ $in{'columns'} }) {
    my (@and, @or, @not);
    foreach my $token (@{ $in{'tokens'} }) {
      my $query = $token;
      
      $query =~ s/\(.*\)$//;
      if ($query =~ /^\+/) {
        $query =~ s/^\+//;
        unless ($in{'case'}) {
          $query = lc($query);
          $query =~ s/([a-z])/\[$1\u$1\]/g;
        }
        $query =~ s/\'/\'\'/g;
        $query = "[[:<:]]".$query."[[:>:]]" if ($in{'whole'});
        
        if ($in{'like'}) {
          push(@and, "$field LIKE BINARY '%$query%'");
        } else {
          push(@and, "$field REGEXP BINARY '$query'");
        }
      } elsif ($query =~ /^\-/) {
        $query =~ s/^\-//;
        unless ($in{'case'}) {
          $query = lc($query);
          $query =~ s/([a-z])/\[$1\u$1\]/g;
        }
        $query =~ s/\'/\'\'/g;
        $query = "[[:<:]]".$query."[[:>:]]" if ($in{'whole'});

        if ($in{'like'}) {
          push(@not, "$field LIKE BINARY '%$query%'");
        } else {
          push(@not, "$field REGEXP BINARY '$query'");
        }
      } else {
        $query =~ s/^\|//;
        unless ($in{'case'}) {
          $query = lc($query);
          $query =~ s/([a-z])/\[$1\u$1\]/g;
        }
        $query =~ s/\'/\'\'/g;
        $query = "[[:<:]]".$query."[[:>:]]" if ($in{'whole'});

        if ($in{'like'}) {
          push(@or, "$field LIKE BINARY '%$query%'");
        } else {
          push(@or, "$field REGEXP BINARY '$query'");
        }
      }
    }
    
    my $where  = join(" AND ", @and);
       $where .= " AND NOT " if ($where && scalar(@not));
       $where .= join(" AND NOT ", @not);
       $where .= " AND " if ($where && scalar(@or));
       $where .= "(".join(" OR ", @or).")" if (scalar(@or));
    push(@Where, "(".$where.")");
  }

  return "(".join(" OR ", @Where).")";
}

###############################################################################
# BuildRegExp subroutine
sub BuildRegExp {
  my $self = shift;
  my %in = (
    "tokens"  => undef,
    "columns" => [],
    "case"    => 0,
    "whole"   => 0,
    @_
  );

  $in{'tokens'} = $self->ParseQuery($in{'tokens'}) unless (ref($in{'tokens'}));

  my @Where;
  foreach my $field (@{ $in{'columns'} }) {
    my (@and, @or, @not);
    foreach my $token (@{ $in{'tokens'} }) {
      my $query = $token;
      
      if ($query =~ /^\+/) {
        $query =~ s/^\+//;
        $query = quotemeta($token);
        $query = "\\b".$query."\\b" if ($in{'whole'});

        $query  = "\$in{'$field'} =~ /$query/";
        $query .= "i" unless ($in{'case'});

        push(@and, $query);
      } elsif ($query =~ /^\-/) {
        $query =~ s/^\-//;
        $query = quotemeta($query);
        $query = "\\b".$query."\\b" if ($in{'whole'});

        $query  = "\$in{'$field'} =~ /$query/";
        $query .= "i" unless ($in{'case'});

        push(@not, $query);
      } else {
        $query =~ s/^|//;
        $query = quotemeta($query);
        $query = "\\b".$query."\\b" if ($in{'whole'});

        $query  = "\$in{'$field'} =~ /$query/";
        $query .= "i" unless ($in{'case'});

        push(@or, $query);
      }
    }

    my $where  = join(" && ", @and);
       $where .= " && " if ($where && scalar(@not));
       $where .= "!(".join(" || ", @not).")" if (scalar(@not));
       $where .= " && " if ($where && scalar(@or));
       $where .= "(".join(" || ", @or).")" if (scalar(@or));
    push(@Where, "(".$where.")");
  }

  return "sub { my \%in = \%{\$_[0]}; return (".join(" || ", @Where)."); }";
}

###############################################################################
# BuildCounter subroutine
sub BuildCounter {
  my $self = shift;
  my %in = (
    "tokens"    => undef,
    "columns"   => [],
    "case"      => 0,
    "whole"     => 0,
    "multiply"  => 0,
    @_
  );

  $in{'tokens'} = $self->ParseQuery($in{'tokens'}) unless (ref($in{'tokens'}));

  my @Where;
  foreach my $field (@{ $in{'columns'} }) {
    foreach my $token (@{ $in{'tokens'} }) {
      next if ($token =~ /^\-/);

      my $query = $token;
         $query =~ s/^\+//;
         $query =~ s/^\|//;
      
      my $multiply;
        ($multiply) = ($query =~ /\((.*)\)$/);
      
      $query =~ s/\(.*\)$//;
      $query = quotemeta($query);
      $query = "\\b".$query."\\b" if ($in{'whole'});
        
      $query  = "\$tmp = () = (\$in{'$field'} =~ /$query/g";
      $query .= "i" unless ($in{'case'});
      $query .= ");";
      $query .= " \$tmp = \$tmp * $multiply;" if ($in{'multiply'} && $multiply);
      $query .= " \$Count += \$tmp;";
        
      push(@Where, $query);
    }
  }

  return "sub { my \%in = \%{\$_[0]}; my (\$Count, \$tmp); ".join(" ", @Where)." return \$Count; }";
}

###############################################################################
# BuildRelevancer subroutine
sub BuildRelevancer {
  my $self = shift;
  my %in = (
    "tokens"  => undef,
    "columns" => [],
    "case"    => 0,
    "whole"   => 0,
    @_
  );

  $in{'tokens'} = $self->ParseQuery($in{'tokens'}) unless (ref($in{'tokens'}));

  my (@Where, %Words);
  foreach my $field (@{ $in{'columns'} }) {
    foreach my $token (@{ $in{'tokens'} }) {
      next if ($token =~ /^\-/);

      my $query = $token;     
         $query =~ s/^\+//;
         $query =~ s/^\|//;
         $query =~ s/\(.*\)$//;
      
      my $word = $query;
      
      $query = quotemeta($query);
      $query = "\\b".$query."\\b" if ($in{'whole'});
      
      $query  = "\$Count{'$word'} = 1 if (\$in{'$field'} =~ /$query/";
      $query .= "i" unless ($in{'case'});
      $query .= ");";
      
      push(@Where, $query);
      $Words{$word} = 1;
    }
  }

  return "sub { my \%in = \%{\$_[0]}; my \%Count; ".join(" ", @Where)." my \$Return = (scalar(keys \%Count) / ".scalar(keys %Words).") * 100; \$Return = 100 if (\$Return > 100); return \$Return; }";
}

###############################################################################
# ParseQuery subroutine
sub ParseQuery {
  my $self = shift;
  my ($Query) = @_;

  my $Quotes = "\\'\\\"";

  my @Tokens;
  my ($Quote, $Quoted, $Unquoted, $Delim, $Word);
  
  while (length($Query)) {
	  ($Quote, $Quoted, undef, $Unquoted, $Delim, undef) = 
	    $Query =~ m/
	      ^([$Quotes]) ((?:\\.|(?!\1)[^\\])*) \1 ([\000-\377]*) |
	      ^((?:\\.|[^\\$Quotes])*?) (\Z(?!\n)|\s+|(?!^)(?=[$Quotes])) ([\000-\377]*)
      /ix;

    last unless ($Quote || length($Unquoted) || length($Delim));

    $Query = $+;
    $Unquoted =~ s/^\s+//;
    $Unquoted =~ s/\s+$//;

    if (defined($Quote)) {
      if (length($Word)) {
        $Word .= $Quoted;
      } else {
        $Word .= "\e".$Quoted;
      }
    } else {
      $Word .= $Unquoted;
    }

    push(@Tokens, $Word) if (length($Word) && (length($Delim) || !length($Query)));
    undef($Word) if (length($Delim));
  }
  
  return \@Tokens;
}

1;